/*! 
 * simple 1.0.8 - JavaScript framework 
 * 	http://code.google.com/p/simple-javascript/
 * (c) 2009 baboo wang <istxwang@gmail.com>
 * simple.js is freely distributable under the terms of an MIT-style license.
 */
 (function(window){
 var
 	//event types
 	events = ['abort', 'blur', 'change', 'click', 'dblclick', 'error', 'focus', 'keydown', 'keyup', 'load', 'mousedown', 'mousemove', 'mouseout', 'mouseleave', 'mouseenter', 'mouseover', 'mouseup', 'reset', 'resize', 'scroll', 'select', 'submit', 'unload'],
 	
 	htmlMatch = /^<(.|\s)+>$/,
 	
 	_$ = window.$,
 	
 	$ = window.$ = window.$imple = function () {
 		return $.__init.apply (this, arguments);
 	};

//Variable $ in window scope may be defined in other place,
//then you can call this function to rename the library's name.
//Btw, you can always use the variable $imple,
//because i think it is not like variable $ so commonly used,
//at least i've never seen : ).
$.noConflict = function () {
	if (_$)
		window.$ = _$;
	return $;	
};

$.extend = function (destination, source) {
	if (source)
		for (var pro in source)
			destination[pro] = source[pro];
	return destination;
};

// Function property
$.extend(Function.prototype, {
	bind: function () {
		var _t = this, args = $.A(arguments), o = args.shift();
		return function () {
			return _t.apply(o, args.concat($.A(arguments)));	
		};
	},

	 delay: function () {
	 	var _t = this, args = $.A(arguments), seconds = args.shift() * 1000;
	 	return window.setTimeout(function () {
	 		return _t.apply(_t, args);
	 	}, seconds);
	},
	
	//special(some arguments fixed)
	special: function () {
		var _t = this, args = $.A(arguments),
		ordered = args.indexOf(undefined) == -1;
		
		return function () {
			var _args = $.A(arguments), final_args = args.slice();
			if (ordered) 
				final_args = final_args.concat(_args);
			else
				for (var i = 0, length = final_args.length; i < length; i ++)
					if (final_args[i] === undefined)
					final_args[i] = _args.shift();
			if (_args.length) final_args = final_args.concat(_args);
			return _t.apply(this, final_args);	
		};	
	}	
});

//String prototype
$.extend(String.prototype, {
  	
  	decode: function () { return decodeURIComponent(this) },
 
  	encode: function () { return encodeURIComponent(this) },
  
	format: function () { 
		var s = this, length = arguments.length; 
		while (--length >= 0)  s = s.replace(new RegExp('\\{' + length + '\\}', 'g'), arguments[length]);
		return s; 
	},
	
	stripTags: function() {
    		return this.replace(/<\/?[^>]+>/gi, '');
  	},
  	
	blank: function () { return /^[\s　]*$/.test(this) },
	
	//simple template engine
	process: function (dataset, context) { 
		context = context || window;
    		return this.replace(/\$\{([^\}]+)\}/g, function(all, exp) {
        			exp = exp.trim();
        			var takens = exp.split('|'), 
        				varname = takens.shift().trim(), 
        				value = (varname in dataset) && dataset[varname] || window[varname] || '', 
        				fun, fun_items;
        			
        			//has functoins to process value, the defined function has the value  as first argument
        			//syntax:  value|fun1:arg2:arg3...|...
        			while (takens.length) {
        				fun_items = takens.shift().split(':');
        				fun = fun_items.shift().trim();
        				if (context[fun])
        					value = context[fun].apply(context, [value].concat(fun_items));	
        				else if (String.prototype[fun]) 
        					value = String.prototype[fun].apply(value, fun_items);	
        			}
        			
        			return value;	
		})
	},
		
	test: function (reg) { return reg.test(this) },
	
	trim: function () { return this.replace(/^[\s　]+|[\s　]+$/g, '') },
	
	 //treat Non-ascii char length 2
	truncate: function(len, tail) {
    		var s = this, i, j;
    		len = len || 30;
    		if(s.replace(/[^\x00-\xff]/g, 'aa').length <= len) return s;
    		if (tail === undefined) tail = '...';
    		len -= tail.length;
    		for (i = 0, j = 0; i < len; ++i) {
    			if (s.charCodeAt(i - j) > 0xff) { ++i; ++j; }	
    		}
    		if ( i != len ) { --j; i -=2; }
    		return  s.slice(0, i - j) + tail;
  	}
});

//Array prototype
$.extend(Array.prototype, {
	each: function (fun) { 
		for (var i = 0; i < this.length; i ++)
			if (fun (this[i], i) === false) 
				break;
		return this;
	},
	
	last: function () { return this.length ? this[this.length - 1] : undefined; },
	
	include: function (obj) { return this.indexOf(obj) != -1; },
	
	grep: function () { return $.grep(arguments[0], this, arguments[1]) },
	
	map: function () { return $.map(arguments[0], this) }
});

if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
  i || (i = 0);
  var length = this.length;
  if (i < 0) i = length + i;
  for (; i < length; i++)
    if (this[i] === item) return i;
  return -1;
};

//browser info
$.Browser = {
	//firefox
	Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1 ,
	
	IE: /msie/.test(navigator.userAgent.toLowerCase()),
	
	Opera: !!window.opera,
	
	//safari, chrome
	WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
	
  	Version: (navigator.userAgent.match( /.+?(?:rv|it|ra|ie)[\/: ]([\d.]+)/i ) || [])[1]
};

$.extend($, {
	__init: function () {
		var args = $.A(arguments);
		//return a simple "Class"
		if (typeof args[0] == 'object' && args[0].__init) {
			var _class = function () { this.__init.apply(this, arguments) };
			_class.prototype = args[0];
			_class.extend = function (obj) {
				var base = $.extend({}, _class.prototype);
				return $($.extend(base, obj));
			};
			return _class;	
		}
		
		//bind to ready
		if (typeof args[0] == 'function')
			return $.ready(args[0]);
		
		if ($.isElement(args[0]) || args[0] == document || args[0] == window || $.isArray(args[0]))
			return new Dollar(args[0], args[1]);
			
		if ($.isString(args[0]) && htmlMatch.test(args[0]))
			return new Dollar($.node(args[0]), args[1]);
		
		if ($.isString(args[0]))
			return new Dollar($.Selector(args[0], args[1], args[2]));
	},
	nothing: function () { return arguments[0] },
	//make array
	A: function (enumobj) {
		if (!enumobj) return [];
		var length = enumobj.length, ret = new Array(length);
		while (length -- ) ret[length] = enumobj[length];		
		return ret;
	},
	
	isDefined: function () { 
		var length = arguments.length;
		while (length--) 
			if (typeof arguments[length] == 'undefined') 
				return false;
		return true;
	},
	isNumber: function(o) { return typeof o == 'number' },
	isString: function (o) { return typeof o == 'string' },
	isArray:	function (o) { return o && o.constructor === Array },
	/*
	isEvent: function (o) { 
		return o  && 
			(String(o.constructor).indexOf('Event') != -1 || 
				o.type && $.events.include(o.type) && typeof o.keyCode != 'undefined');
	},*/
	
	isElement: function (o) { return o && o.nodeType === 1 && o.tagName },
	isFunction: function (o) { return typeof o == 'function' },
	
	grep: function (fun, enumobj, eor) {
		if ($.isString(fun)) {
			try {
				fun =  new Function ('v', 'k', 'return ' + fun);
			} catch (e) { return null; } 
		}
		var ret = [], eor = !!eor, isFunction = $.isFunction(fun), isReg = fun.constructor == RegExp;
		
		//check enumobj is array like object
		if (typeof enumobj.length == 'number')
			for (var i = 0; i < enumobj.length; i++)
				isFunction && (!!fun(enumobj[i], i) ^ eor) && ret.push(enumobj[i]) ||
				//fun  is actually a Regexp
				isReg && (fun.test(enumobj[i]) ^ eor) && ret.push(enumobj[i]);
		return ret;
	},
	map: function (fun, obj) {
		if ($.isString(fun))
			try {
				fun = new Function ('v', 'k', 'return ' + fun);
			} catch (e) { return null }
		
		var ret = [];
		
		//array like object
		if (obj && typeof obj.length == 'number')
			for (var i = 0; i < obj.length; i++)
				ret.push(fun(obj[i], i));
		else if (obj)
			//general object
			for (var pro in obj)
				if (obj.hasOwnProperty(pro))
					ret.push(fun(obj[pro], pro));	
		
		//if map function return {name:'', value:''} like object, then return a 'hash' object
		if (ret.length && ret[0] && $.isDefined(ret[0].name, ret[0].value)) {
			var nret = {}, length = ret.length;
			while (length--) 
				nret[ret[length].name] = ret[length].value;
			ret = nret;
		}
		return ret;
	},
	each: function (obj, fun) {
		var length = obj.length;
		if (undefined == length) {
			for (var pro in obj)
				if (fun(obj[pro], pro) === false)
					break;
		} else {
			for (var i = 0; i < length; i++) 
				if (fun(obj[i], i) === false)
					break;
		}
		return $;
	},
	
	//to store data that object related
	__DATA: {},
	data: function (obj, name, value) {
		if (!obj.guid) obj.guid = $.guid();
		var data = this.__DATA[obj.guid];
		if (name && !data)
			data = this.__DATA[obj.guid] = {};
		if ($.isDefined(value))
			data[name] = value;
		return name ? data[name] : obj.guid;
	},
	
	//return a only string
	guid: (function () { var guid_base = 0; return function () { return "GUID__" + (guid_base ++) } })()
	
}); 

//Dom Element Selector
$.Selector = function () {
	var AttributeFilters = {
		'' : function (attr) { return !!attr },
		//force attr to string
		//true => 'true'
		'!=' : function (attr, expected) { return attr != expected },
		'=' : function (attr, expected) { return attr + '' === expected },
		'~=' : function (attr, expected) { return attr && (' ' + attr + ' ').indexOf(' ' + expected + ' ') >= 0 },
		'|=' : function (attr, expected) { return attr && attr.match(/^[^-]+/g).include(expected) },
		'^=' : function (attr, expected) { return attr && !attr.indexOf(expected) },
		'*=' : function (attr, expected) { return attr && attr.indexOf(expected) > -1 },
		'$=' : function (attr, expected) { return attr && (attr.lastIndexOf(expected) + expected.length) == attr.length }
	};
	
	//check is a attributeFilter or range filter
	var simpleAttributeFilterMatch = /^[_a-zA-Z]\w*$/;
	function isAttributeFilter (v) {
		return v.indexOf ('=') > 0 || (v != 'n' && simpleAttributeFilterMatch.test(v));
	}
	
	var attributeFilterMatch = /^(\w+)(?:(=|!=|~=|\|=|\^=|\*=|\$=)(.+))?/;
	function getAttributeFilterData (expression) {
		var ma = expression.match(attributeFilterMatch)	, 
			attr = RegExp.$1, filterFunc = AttributeFilters[RegExp.$2 || ''], expected = RegExp.$3;
		return [filterFunc, attr, expected];
	}
	var alias_nth_name = {
		'even' : '2n',
		'odd' : '2n+1'	
	};
	var alias_function_name = {
		'first-child' : ['nth-child', '1'],
		'last-child' : ['nth-last-child', '1'],
		first: ['range', '0'],
		last: ['range', 'n'],
		even: ['nth', '2n'],
		odd: ['nth', '2n+1'],
		header: ['match', { tagName: /^H\d$/ }],
		empty: ['match', { func: function (v, elem) { return !elem.firstChild } }],
		parent: ['match', { func: function (v, elem) { return elem.firstChild } }],
		hidden: ['match', { func: function (v, elem) { return 'hidden' == elem.type || $.css(elem, 'display') == 'none' || $.css(elem, 'visibility') == 'hidden' } }],
		visible: ['match', { func: function (v, elem) { return 'hidden' != elem.type && $.css(elem, 'display') != 'none' && $.css(elem, 'visibility') != 'hidden' } }],
		//Form attribute
		checked: ['match', { checked: true }],
		enabled: ['match', { disabled: false } ],
		disabled: ['match', { disabled: true } ],
		selected: ['match', { selected: true }],
		// Form elements
		text: ['match', { type: 'text' }],
		radio: ['match', { type: 'radio' }],
		checkbox: ['match', { type: 'checkbox' }],
		file: ['match', { type: 'file' }],
		password: ['match', { type: 'password' }],
		reset: ['match', { type: 'reset' }],
		submit: ['match', { type: 'submit' }],
		input: ['match', { tagName: /input|select|textarea|button/i }]
	};
	var FuncFilters = {
		__getPositions: function (expression, length, startFromEnd) {
			var num, pos = [];
			expression = alias_nth_name[expression] || expression;
			switch (true) {
				case /^(\d+)$/.test(expression) :
					num = parseInt(RegExp.$1, 10);
					num > 0 && num <= length && pos.push(startFromEnd ? (length - num + 1) : num);
					break;	
				case /^(-)?(\d+)?n([+-]\d+)?$/.test(expression) :
					(function (pos, length) {
						var f = RegExp.$1 == '-' ? -1 : 1,
						mulNum = parseInt(RegExp.$2 || '1', 10),
						addNum = parseInt(RegExp.$3 || '0', 10);
						if (mulNum == 0) {
							if (addNum > 0 && addNum <= length)
								pos.push(startFromEnd ? (length - num + 1) : addNum);
						} else {
							if ( f > 0 )
								for (var i = 0, v; (v = (mulNum * i + addNum)) <= length; i++)
									v > 0 && pos.push(startFromEnd ? (length - v + 1) : v);
							else
								for (var i = 0, v; (v = (-mulNum * i + addNum)) > 0; i++)
									v <= length && pos.push(startFromEnd ? (length - v + 1) : v);
						}
					})(pos, length);	
					break;
				default:
					syntaxError("unknow range filter:" + expression);
			}
			return pos;
		},
		range: function (elems, expression, startFromEnd) {
			var length = elems.length, ret = [], 
				expression = expression.replace(/n/g, length - 1).
					replace(/(\d+)-(\d+)/g, function (all, a, b) { return a - b }) ;
			if (/^[0-9 ]+$/.test(expression)) {
				expression.split(/\s+/).each(function(index){
					index = parseInt(index, 10);
					if (index < length)
						ret.push(elems[startFromEnd ? length - 1 - index : index]);		
				});
			} else if (/^(\d+)\.\.(\d+)$/.test(expression)) {
				var start = parseInt(RegExp.$1, 10), end = parseInt(RegExp.$2, 10);
				end = Math.min(length - 1, end);
				if (startFromEnd) {
					var estart = length - 1 - end, eend = length - 1 - start;
					start = estart, end = eend;	
				}
				if (end >= start) 
					while (start <= end)
						ret.push(elems[start++]);						
			}
			return ret;
		},
		'range-last' : function (elems, expression) {
			return FuncFilters.range(elems, expression, true);	
		},
		nth: function (elems, expression, startFromEnd) {
			var pos = FuncFilters.__getPositions(expression, elems.length, startFromEnd), ret = [];
			pos.sort(function (a, b) { return a > b });
			for (var i = 0, n = pos.length; i < n; i ++)
				ret.push(elems[pos[i] - 1]);
			return ret;
		},
		'nth-last' : function (elems, expression) {
			return FuncFilters.nth(elems, expression, true);
		},
		'nth-child' : function (elems, expression, startFromEnd) {
			var children, elem, pos, parentNode, ret = [], elemMap = {}, pguid, guid;
			for (var i = 0, n = elems.length; i < n; i ++) {
				elem = elems[i];
				parentNode = elem.parentNode;
				guid = $.data(elem);
				pguid = $.data(parentNode);
				if (elemMap[pguid]) {
					if (elemMap[pguid][guid])
						ret.push(elem);
					continue;
				}
				if (parentNode) {
					children = [];
					var nodes = parentNode.children || parentNode.childNodes;
					for (var j = 0, m = nodes.length; j < m; j ++)
						nodes[j].nodeType == 1 && children.push(nodes[j]);
					pos = FuncFilters.__getPositions(expression, children.length, startFromEnd);
					elemMap[pguid] = {};
					pos.each(function(p) { elemMap[pguid][$.data(children[p - 1])] = 1 });
					elemMap[pguid][guid] && ret.push(elem);
				}
			}
			return ret;
		},
		'nth-last-child' : function (elems, expression) {
			return FuncFilters['nth-child'](elems, expression, true);	
		},
		'nth-of-type' : function (elems, expression, startFromEnd) {
			var children, elem, pos, parentNode, ret = [], tagName, elemMap = {}, pguid, guid;
			for (var i = 0, n = elems.length; i < n; i ++) {
				elem = elems[i], tagName = elem.tagName;
				parentNode = elem.parentNode;
				pguid = $.data(parentNode);
				guid = $.data(elem);
				if (elemMap[pguid]) {
					elemMap[pguid][guid] && ret.push(elem);
					continue;	
				}
				if (parentNode) {
					children = $.grep(function (v) { return v.nodeType == 1  && v.tagName == tagName }, parentNode.children || parentNode.childNodes);
					pos = FuncFilters.__getPositions(expression, children.length, startFromEnd); 
					elemMap[pguid] = {};
					pos.each ( function (p) { elemMap[pguid][$.data(children[p - 1])] = 1 } );
					elemMap[pguid][guid] && ret.push(elem);
				}
			}
			return ret;
		},
		'nth-last-of-type' : function (elems, expression) {
			return FuncFilters['nth-of-type'](elems, expression, true);	
		},
		'only-of-type' : function (elems, expression) {
			var elem, ret = [], children, parentNode, elemMap = {}, tagName, guid;
			for (var i = 0, n = elems.length; i < n; i ++) {
				elem = elems[i], parentNode = elem.parentNode, tagName = elem.tagName;
				if (parentNode) {
					guid = $.data(parentNode) + tagName;
					if (elemMap[guid]) continue;
					children = parentNode.children || parentNode.childNodes;
					var k = 0;
					for (var j = 0, m = children.length; j < m; j ++) {
						if (children[j].nodeType == 1 && 
							children[j].tagName == tagName &&
							(++k == 2)
							) break;
					}
					if ( k == 1) {
						elemMap[guid] = 1;
						ret.push(elem);
					}
				}	
			}
			return ret;	
		},
		'only-child' : function (elems) {
			var elem, ret = [], children, parentNode;
			for (var i = 0, n = elems.length; i < n; i ++) {
				elem = elems[i], parentNode = elem.parentNode;
				if (parentNode) {
					children = parentNode.children || parentNode.childNodes;
					var k = 0;
					for (var j = 0, m = children.length; j < m; j ++)
						if (children[j].nodeType == 1 && (++k == 2))  break;
					k == 1 && ret.push(elem);
				}
			}
			return ret;	
		},
		'cd-parent' : function (elems) {
			var ret = [], elemMap = {}, elem, guid;
			for (var i = 0, n = elems.length; i < n; i ++) {
				elem = elems[i].parentNode;
				if (elem) {
					guid = $.data(elem);	
					if (elemMap[guid]) continue;
					elemMap[guid] = 1;
					ret.push(elem);
				}
			}
			return ret;
		},
		up: function (elems, expression) {
			var ret = [], elem, tagName = expression.toUpperCase(), elemMap = {}, guid;
			for (var i = 0, n = elems.length; i < n; i ++) {
				elem = elems[i];
				do {
					if (elem.tagName == tagName) break;
				} while (elem = elem.parentNode);
				if (elem) {
					guid = $.data(elem);
					if (!elemMap[guid]) {
						ret.push(elem);
						elemMap[guid] = 1;
					}
				}
			}
			return ret;
		},
		contains: function (elems, expression) {
			var elem, expression = expression.replace(/^['"]|['"]$/g, ''), ret = [];
			for (var i = 0, n = elems.length; i < n; i ++) {
				elem = elems[i];
				(elem.textContent || elem.innerText || elem.innerHTML.stripTags() || '').indexOf(expression) >= 0 &&
				ret.push(elem);
			}
			return ret;
		},

		match: function (elems, matches) {
			var ret = [], elem, props = $.keys(matches), expression, isOk, propVal;
			for (var i = 0, n = elems.length; i < n; i ++) {
				elem = elems[i], isOk = true;
				for (var j = 0, m = props.length; j < m; j ++) {
					expression = matches[props[j]], propVal = $.attr(elem, props[j]);
					switch (typeof expression) {
						case 'string' :
						case 'boolean':
							if (propVal != expression)
								isOk = false;
							break;
						case 'function' :
							if (!expression(propVal, elem))
								isOk = false;
							break;
						default:
							if (expression.constructor == RegExp && !expression.test(propVal))
								isOk = false;
							break;
					}
					if (isOk == false) break;
				}
				isOk && ret.push(elem);
			}
			return ret;	
		},
	
		__subPathRelation: function (elems, expression, includeIt) {
			var subElems = getElements(expression, elems, '');
			return elems.grep(function (elem) {
				var ret = subElems.include(elem);	
				return includeIt ? ret : !ret;
			});
		},
		has: function (elems, expression) {
			return FuncFilters.__subPathRelation(elems, expression, true);
		},
		not: function (elems, expression) {
			return FuncFilters.__subPathRelation(elems, expression, false);
		}
	};
	
	function syntaxError (msg) {
		throw "Selector Syntax Error: " + msg;	
	}
	
	var BaseFilter = $({
		__init: function (c, context) {
			this.expression = c;
			this.context = context;	
		},
		eat: function (c) {
			if (this.testRegExp.test(c) && (this.expression += c))
				return true;
			return false;	
		}
	});
	
	var TagFilter = BaseFilter.extend({
		testRegExp : /[a-zA-Z0-9]/,
		filter: function (elems) {
			var ret = [], elem, tagName = this.expression.toUpperCase();
			
			if (tagName != '*' && !/^[A-Z0-9]+$/.test(tagName))
				throw "unknow TagName:" + tagName;
			//has path context such like ~,+,>
			//we use tagName attribute filter :)
			if (!this.context || (this.context && this.context != '*')) {
				return new AttributeFilter(
					tagName == '*' ? 'tagName' : 'tagName=' + tagName, 
				this.context).filter(elems);	
			}
			var elemMap = {}, guid;		
			for (var i = 0, n = elems.length; i < n ; i ++) {				
				var matched = elems[i].getElementsByTagName(tagName);
				
				//only one element, don't check the intersection
				if (elems.length == 1) return $.A(matched);
				
				if (matched) {
					for (var j = 0, m = matched.length; j < m; j ++) {
						elem = matched[j], guid = $.data(elem);
						//means that the element' parents has be processed
						//in the elems, parent is alway before the child, if then both exists
						if (elemMap[guid]) break;
						(elemMap[guid] = 1) && ret.push(elem);
					}
				}
			}
			return ret;	
		},
		toString: function () {
			return 'TagName:' + (this.context && this.context != '*' ? this.context + ' ' : '') + this.expression;	
		}
	});
	
	var AttributeFilter = BaseFilter.extend({
		eat: function (c) {
			if (c != ']' && (this.expression += c))
				return true;
			if (c == ']') return undefined;
			return false;	
		},
		filter: function (elems) {
			if (!isAttributeFilter(this.expression))
				return new FunctionFilter('range', this.expression).filter(elems);
			var filterData = getAttributeFilterData(this.expression), 
				checker = filterData[0], attrName = filterData[1], expected = filterData[2];
			var ret = [], elem;
			
			if (this.context == '*')
				elems = new TagFilter('*', '*').filter(elems),
				this.context = '';
			
			if (this.context) {
				switch (this.context) {
					case '>' :
						for (var i = 0, n = elems.length; i < n; i ++) {
							if (elems[i].hasChildNodes()) {
								var children = elems[i].childNodes;
								for (var j = 0, m = children.length; j < m; j ++) {
									children[j].nodeType == 1 && 
									checker($.attr(children[j], attrName), expected) &&
									ret.push(children[j]);
								}
							}
						}
						break;
					case '~' :
						for (var i = 0, n = elems.length; i < n; i ++) {
							elem = elems[i];
							while (elem = elem.nextSibling) {
								if (elem.nodeType == 1 && 
								checker($.attr(elem, attrName), expected)) {
									if (ret.include(elem)) break;
									ret.push(elem);
								}
							}
						}
						break;
					case '+' :
						for (var i = 0, n = elems.length; i < n; i ++) {
							elem = elems[i];
							while (elem = elem.nextSibling) {
								if (elem.nodeType == 1) break;	
							}	
							if (elem && checker($.attr(elem, attrName), expected))
								ret.push([elem]);
						}
						break;
					default:
						syntaxError('unknow path context:' + this.context);	
				}
				return ret;	
			}
			
			for (var i = 0, n = elems.length; i < n ; i ++)
				checker($.attr(elems[i], attrName), expected) &&
				ret.push(elems[i]);
			
			return ret;
		},
		toString: function () {
			return "Attribute: " + this.context + ' ' + this.expression;	
		}
	});
	
	var IDFilter = BaseFilter.extend({
		testRegExp: /[a-zA-Z0-9_-]/,
		
		filter: function (elems) {
			if (elems.length == 1 && elems[0] == document) {
				var elem = document.getElementById(this.expression);
				return elem ? [elem] : [];	
			}
			return new AttributeFilter('id=' + this.expression, this.context).filter(elems);
		},
		toString: function () {
			return "ID:" + this.context + '#' + this.expression;	
		}
	});
	
	var ClassNameFilter = BaseFilter.extend({
		testRegExp: /[a-zA-Z0-9_-]/,
		
		filter: function (elems) {
			return new AttributeFilter('class~=' + this.expression, this.context).filter(elems);	
		},
		toString: function () {
			return "ClassName:" + this.expression;	
		}
	});
	
	var FunctionFilter = $({
		__init: function (funcname, params, context) {
			this.funcname = funcname || '';
			this.params = params || '';
			this.context = context;
			this.eatStatus = 0;
		},
		eat: function (c) {
			if (this.eatStatus == 0) {
				if (/[\w-]/.test(c)) {
					this.funcname += c;	
					return true;
				}
				if (c == '(') {
					this.eatStatus = 1;
					return true;	
				}
				return false;
			} else {
				if (c != ')' && (this.params += c))
					return true;
				return undefined;	
			}
		},
		filter: function (elems) {
			if (alias_function_name[this.funcname]) {
				var alias = [].concat(alias_function_name[this.funcname]);
				this.funcname = alias[0];
				this.params = alias[1] || this.params;	
			}
			var func = FuncFilters[this.funcname];
			if (!func)
				syntaxError("unknow function filter:" + this.funcname);
			if (this.context == '*')
				elems = new TagFilter('*', '*').filter(elems);
			return func(elems, this.params);
		},
		toString: function () {
			return "Funciotn:" + this.funcname + "(" + this.params + ")";	
		}
	});
	
	function getFilter (c, context) {
		var filter;
		switch (c) {
			case '.' :
				filter = new ClassNameFilter('', context);
				break;
			case '#' :
				filter = new IDFilter('', context);
				break;
			case ':' :
				filter = new FunctionFilter('', '', context);
				break;
			case '[' :
				filter = new AttributeFilter('', context);
				break;
			default:
				filter = new TagFilter(c, context);
		}
		return filter;	
	}
	
	function getFilters (expression, initContext) {
		var c, filters = [], context = (initContext === undefined ? '*' : initContext), filter = null;
		debug('expression:' + expression);
		debug('initContext:' + context);
		for (var i = 0, n = expression.length; i < n; i ++) {
			c = expression.charAt(i);
			if (context) {
				if (c == ' ') continue;
				if ('>~+'.indexOf(c) >= 0) {
					context = c;
					continue;
				}
				filter = getFilter(c, context);
				context = '';	
				continue;
			}
			if (filter != null) {
				var ret = filter.eat(c);
				if (ret) continue;
				filters.push(filter);
				filter = null;
				context = '';
				if (ret === false)
					i --;
				continue;		
			} else {
				if (c == ' ') {
					context = '*';
					continue;	
				} else {
					filter = getFilter(c, '');
				}	
			}
		}
		if (filter != null)
			filters.push(filter);
		return filters;	
	}
	
	function getElements (path, context, pathContext) {
		var filters = getFilters(path, pathContext);
		for (var i = 0; i < filters.length; i ++)
			debug('filter:' + filters[i]);
		var ret = $.isArray(context) ? context : [context];
		for (var i = 0, n = filters.length; i < n; i ++) {
			ret = filters[i].filter(ret);
			if (ret.length == 0) break;
		}
		return ret;
	}
	
	function debug (s) {
		//console.log(s);
	}
	function selector (path, context, isClose) {
		var elems = [];
		context = context || document;
		var paths = path.split(',');
		for (var i = 0; i < paths.length; i ++) {
			elems = elems.concat(getElements(paths[i], context, isClose ? '' : '*'));
		}
		return elems;
	}
	selector.extend = function (obj) {
		$.extend(FuncFilters, obj);
		return selector;
	};
	selector.alias = function (alias, isnth) {
		$.extend(
			isnth ? alias_nth_name : alias_function_name,
			alias);
		return selector;
	};
	return selector;
}();

$.extend($, {
	keys: $.map.special(function(v, k){return k}),
	values: $.map.special(function(v, k){return v})
});

//make it simple to use
['Gecko=>isGecko', 'IE=>isIE', 'Opera=>isOpera', 'WebKit=>isWebKit', 'Version=>browserVersion'].each(function (v) {
	var takens = v.split('=>');
	$[takens[1]] = $.Browser[takens[0]];	
});

var Dollar = function (elems, previous) {
	this.previous = previous;
	return this.setArray([].concat(elems || []));			
};

Dollar.prototype = $.plugins = {
	//code from jquery
	setArray: function (elems) {
		this.length = 0;
		Array.prototype.push.apply(this, elems);	
		return this;
	},
	
	splice: function () {
		var elems = this.get();
		
		Array.prototype.splice.apply(elems, $.A(arguments));
		
		return new Dollar(elems, this);

	},

	back: function (front) {
		if (!front)
			return this.previous || this;
		var $es = this;
		while ($es.previous)
			$es = $es.previous;
		return $es;	
	},
	
    end: function () { return this.back(true) },

	//use it like 
	//css(name)  - get the style value
	//css(name, value) - set a style
	//css(styletext) - set styles
	//css(styleobj) - set styles
	css: function () {
		var args = $.A(arguments), index = (typeof args[0] == 'number' ? args.shift() : false),
			style = args.shift(), get;
		
		//convert to styletext
		args.length && 
		'number string'.indexOf(typeof args[0]) >= 0 && 
		typeof style == 'string' && 
		(style += ':' + args.shift());	
		
		get = $.isString(style) && style.indexOf(':') < 0;
		
		if (get) {
			var ret;
			if (index === false)
				ret = $.map(function (elem) { return $.css(elem, style, args[0]) }, this);
			else 
				ret = index < this.length && $.css(this[index], style, args[0]);
			return !$.isArray(ret) || ret.length > 1 ? ret : ret[0];
		}
		
		//parse the style text to style object
		if ($.isString(style)) {
			var str = style, style = {};
			str.split(';').each( function( rule ) {
				if (!rule.blank()) {
					var takens = rule.split(':');
					if (takens.length == 2) {
						style[takens[0].trim()] = takens[1].trim();
					}	
				}
			});
		}
		//set styles and return Dollar
		for (var pro in style) {
			if (index === false)
				this.each(function(elem){
					$.css(elem, pro, style[pro]);
				});
			else
				index < this.length && $.css(this[index], pro, style[pro]);
		}
		return this;
	},
	
	method: function () {
	        var args = $.A(arguments), index = false, ret, i;
	        
	        //you can specify the first paramter a number as element index, 
	        //to designate the element to be operated
	        if ($.isNumber(args[0])) index = args.shift();
	       
	        //if you want return value, add prefix string to the function name
	        //	like $e.method('$css', 'color');
	        var ma = args.shift().match(/^(\$?)(.+)$/),
			wantRetVal = !!ma[1],
	            	//attribute name or method name in $.ems
	            	name = ma[2];
	            
	        if (this.length == 0)
	            	return wantRetVal ? undefined : this;
	            
            	if (name in $.ems) {
                	var method = $.ems[name];
                	if (false !== index)
                    		ret = method.apply(this, [this[index]].concat(args));
                	else {
                    		for (i = 0, ret = []; i < this.length; i ++)
                        		ret.push(method.apply(this, [this[i]].concat(args)));
                        }
            	} else {
            		throw "Method:'{0}' not found in $.ems".format(name);
            	}
	    	return wantRetVal ? ret : this;
    	},
    	multipleEffect: function (sets) {
    		if (!sets.style)
    			sets = { style : sets };
    		sets = $.extend({
    			delay: 0.0,
    			speed: 0.1
    		}, sets);
    		var delay = sets.delay, self = this, onAllFinish = sets.onAllFinish, lastIndex = this.length - 1;
    		this.each(function(elem, index){
    			var _sets = $.extend({}, sets);
    			_sets.delay = delay + index * sets.speed;
    			if (onAllFinish && index == lastIndex) {
    				var originOnFinish = _sets.onFinish;
    				_sets.onFinish = function () {
    					originOnFinish && originOnFinish.call(this);
    					onAllFinish.call(self);
    				};
    			}
    			$.ems.effect.call(self, elem, _sets);
    		});
    		return this;
    	},
	each: function (func) { $.each(this, func); return this; },
	map: function (func) { return $.map(func, this) },
    	grep: function (func, eor) { return $($.grep(func, this, eor)) },
    	get: function (index) {
        	return index === undefined ? $.A(this) : this[index];
    	},
    	is: function (selector) {
    		return this.find(selector, true).length > 0;	
    	},
    	find: function (path, isClose) {
    		return $($.Selector(path, this.get(), isClose), this);	
    	}
};

$.plugins.$ = $.plugins.find;

$.plugins.extend  = function (plugins) {
	$.extend(Dollar.prototype, plugins);
	return $;
};


//call function groups in circle
$.Toggle = $({
	__init: function () {
		this.funs = $.isArray(arguments[0]) ? arguments[0] : $.A(arguments);
		this.pos = 0;
	},

	call: function (args, obj) {
		args = [].concat(args);
		this.funs[this.pos ++ % this.funs.length].apply(obj || null, args);
	}
});

var $e = function (elem) {
	return $.isString(elem) && (!elem.indexOf('<') ? $.node(elem) : $.text(elem) ) ||
	elem.$ && elem[0] ||
	elem;	
};

//element methods
$.ems =  {
    parent: function (elem) {
        return new Dollar((elem && elem.parentNode) || [], this);
    },
	next : function (elem) {
		while (elem && (elem = elem.nextSibling)) {
			if (elem.nodeType == 1)
				break;	
		}
		return new Dollar(elem || [], this);
	},
	prev : function (elem) {
		while (elem && (elem = elem.previousSibling)) {
			if (elem.nodeType == 1)
				break;	
		}
		return new Dollar(elem || [], this);
	},
	append: function (elem, child) {
		elem && elem.appendChild($e(child));
		return this;			
	},
	appendTo : function (elem, parent) {
		elem && parent.appendChild(elem);
		return this;	
	},
	prepend: function (elem, child) {
		elem && elem.insertBefore($e(child), elem.firstChild);
		return this;
	},
	prependTo : function (elem, parent) {
		elem && parent.insertBefore(elem, parent.firstChild);	
		return this;
	},
	before: function (elem, child) {
		elem && elem.parentNode.insertBefore($e(child), elem);	
		return this;
	},
	after: function (elem, child) {
		elem && elem.parentNode.insertBefore($e(child), elem.nextSibling);
		return this;
	},
	attr: function (elem, name, value) {
		//name is actually a name-value pairs object
		if (typeof name == 'object') {
			$.each(name, function (value, attr) {
				$.attr(elem, attr, value);	
			});
			return this;
		}
		var ret = $.attr(elem, name, value);
		if (value !== undefined)
			return this;
		else
			return ret;
	},
   	data: function (elem, name, value) {
        return $.data(elem, name, value);
    },
	//className operation
	//(+)?className : add className
	//-className : remove className
	//className1 => className2 : replace className1 to className2
	//@className : check if contain className
	className: function (elem, querystr) {
		var args = $.A(arguments);
		elem = args.shift(), querystr = args.join('');
		querystr = querystr.replace(/^(.+?)\s*=>\s*(.+)$/, '~$1 $2');
		var ma = querystr.match(/^(\W)?([\w-]+)\s*([\w-]+)?$/),
			originClassName = elem.className || '', 
			reg = new RegExp('(^|\\s+)(' + ma[2] + ')($|\\s+)');
		if (!ma) return false;
		switch (ma[1]) {
			case '@' : 
				return  reg.test(originClassName);
			case '~'  : 
				elem.className = originClassName.replace(reg, '$1' + ma[3] + '$3'); 
				break;
			case '-' : 
				elem.className = originClassName.replace(reg, '$1$3'); 
				break;
			case '+' : 
			default: 
				if (! $.ems.className(elem, '@' + ma[2])) elem.className = originClassName + (originClassName == '' ? '' : ' ') + ma[2];
		}
		return this;
	},
	hasClass: function (elem, n) { return $.ems.className(elem, '@', n) },
	addClass: function (elem, n) { $.ems.className(elem, '+', n); return this },
	removeClass: function (elem, n) { $.ems.className(elem, '-', n); return this },
	toggleClass: function (elem, n) { $.ems.className(elem,  $.ems.className(elem, '@', n) ? '-' : '+', n); return this },
	replaceClass: function (elem, originClass, replaceClass) { $.ems.className(elem, originClass, '=>', replaceClass); return this },
	
	text: function (elem, value) {
		if (value === undefined)
			return elem && (elem.textContent || elem.innerText || elem.innerHTML.stripTags()) || '';
        if (elem) {
		    elem.innerHTML = '';
		    elem.appendChild($.Builder.text(value + ''));
        }
		return this;
	},
	
	html: function (elem, value) {
		if (value === undefined)
			return elem && elem.innerHTML || '';
		else if (elem)
			elem.innerHTML = "" + value;
		return this;
	},
	
	hide: function (elem, mode, handle) {
        var real_handle = handle || (typeof mode == 'function' && mode);

        if ($.css(elem, 'display') == 'none') {
            real_handle && real_handle.call(elem);
            return this;
        }
        
        if (mode) {
            mode = mode == 'slow' ? 1.5 : (mode == 'fast' ? 0.5 : mode);
            $.ems.ehide(elem, mode, handle);
        } else {
            elem.style.display = 'none';
        }

		return this;
	},
	
	show: function (elem, mode, handle) {
        var real_handle = handle || (typeof mode == 'function' && mode);

        if ($.css(elem, 'display') != 'none') {
            real_handle && real_handle.call(elem);
            return this;
        }
        
        if (mode) {
            mode = mode == 'slow' ? 1.5 : (mode == 'fast' ? 0.5 : mode);
            $.ems.eshow(elem, mode, handle);
        } else {
    		elem.style.display = '';	
        }

		return this;
	},
	
	bind: function (elem, types, handler) {
		$.bind(elem, types, handler);
		return this;
	},
	
	unbind: function (elem, types, handler) {
		$.unbind(elem, types, handler);
		return this;
	},
	
	effect: function (elem, sets) {
		if (!sets.style)
			sets = { style : sets };
		if (sets.queue !== false)
			sets.queue = $.data(elem);
		sets.guid = $.data(elem);
		sets.onAdd = extendElementEffect.bind(elem, sets);
		$.Effect[sets.queue ? 'addToQueue' : 'add'](sets);	
		return this;
	},
	
	stop: function (elem, clearQueue, toEnd) {
		var guid = $.data(elem);
		clearQueue && $.Effect.removeQueue(guid);
		$.Effect.remove(guid, toEnd);
		return this;	
	},
	
	update: function (elem, url, sets) {
		sets = sets || {};
		sets.onOk = function (data) {
			if (sets.process)
				data = sets.process(data);
			elem.innerHTML = data;
		};
		if (sets.onFail) {
			sets._onFail = sets.onFail;
			sets.onFail = function () {
				sets._onFail.apply(elem);	
			};
		}
		sets.type && sets.type == 'json' ?
			new $.Json(url, sets) :
			new $.Ajax(url, sets);
		return this;
	},
	
	remove: function (elem) {
		elem.parentNode.removeChild(elem);
		return this;
	},
	
	empty: function (elem) {
		$('>*', elem).method('$remove');
		while (elem.firstChild)
			elem.removeChild(elem.firstChild);
		return this;
	},
	
	toggle: function (elem, eventType) {
		//toggle element display
		if (! (typeof eventType == 'string' && events.include(eventType.split(/\s+/)[0]))) {
            $.ems[$.css(elem, 'display') == 'none' ? 'show' : 'hide'](elem, arguments[1], arguments[2]);
			return this;
		}
		
		//bind event in toggle call functions
		var args = $.A(arguments), elem = args.shift(), eventType = args.shift(), 
		funcs = args, toggle = new $.Toggle(funcs);
		$.bind(elem, eventType, function (event) {
			toggle['call'](event, elem);	
		});
		return this;
	},
	
	val: function (elem, value) {
		if (value === undefined) {
			if (elem.tagName == 'SELECT') {
				value = $('option:selected', elem).method('$attr', 'value');
				if (elem.type == "select-multiple" && ! $.isArray(value))
					value = [value];
			} else
				value = elem.value;
			return value;
		}
		if (elem.tagName == 'SELECT') {
			value = [].concat(value);
			$.each(elem.options, function (option) {
				if (value.include(option.value)) {
					option.selected = true;
				}
			});	
		} else
			elem.value = value;
		return this;
	},
	
	_val : function (value) {
		if (value === undefined)
			return false;
		values = [].concat(value);
		return this.each(function(elem) {
			if (['checkbox', 'radio'].include(elem.type)) {
				elem.checked = values.include(elem.value);
			} else if (elem.tagName == 'SELECT') {
				$.A(elem.options).each(function (option) {
					option.selected = values.include(option.value);	
				});
				if (values.length == 0)
					elem.selectedIndex = -1;
			} else
				elem.value = values[0];
		});
	},
	
	position: function (elem) {
		 var top = 0, left = 0;
		do {
			top += elem.offsetTop  || 0;
		  	left += elem.offsetLeft || 0;
		  	elem = elem.offsetParent;
		  	if (elem) {
		    		if (elem.tagName == 'BODY') break;
		    		var p = $.css(elem, 'position');
		    		if (p !== 'static') break;
		  	}
		} while (elem);
		return {top: top, left: left};	
	},
	
	//origin code from prototype.js
	dimentions: function(elem) {
		var display = $.css(elem, 'display');
		if (display != 'none' && display != null)
		  return {width: elem.offsetWidth, height: elem.offsetHeight};
		
		var els = elem.style;
		var originalVisibility = els.visibility;
		var originalPosition = els.position;
		var originalDisplay = els.display;
		els.visibility = 'hidden';
		els.position = 'absolute';
		els.display = 'block';
		var originalWidth = elem.clientWidth;
		var originalHeight = elem.clientHeight;
		els.display = originalDisplay;
		els.position = originalPosition;
		els.visibility = originalVisibility;
		return {width: originalWidth, height: originalHeight};
	}
};

$.ems.offset = (document.documentElement["getBoundingClientRect"] ?
function (e) {
	var rect = e.getBoundingClientRect(), doc = e.ownerDocument,
		top = rect.top + (self.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop),
		left = rect.left + (self.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft);
	return { left: left, top: top };
} :
function (e) {
	var offsetParent = e.offsetParent, doc = e.ownerDocument, docElem = doc.documentElement, 
		defaultView = doc.defaultView, body = doc.body,
		prevElem = e, prevComputedStyle, top = e.offsetTop, left = e.offsetLeft;
	
	while ( (e = e.parentNode) && e != body) {
		top -= e.scrollTop, left -= e.scrollLeft;
		if (e === offsetParent) {
			e += offsetParent.offsetTop, left += offsetParent.offsetLeft;
			offsetParent = e.offsetParent;	
		}
		prevElem = e;
	}
	
	prevComputedStyle = defaultView.getComputedStyle(prevElem);
	if ("relative static".indexOf(prevComputedStyle.position) >= 0)
		top += body.offsetTop,
		left += body.offsetLeft;
	
	if (prevComputedStyle.position == 'fixed')
		top += Math.max(docElem.scrollTop, body.scrollTop),
		left += Math.max(docElem.scrollLeft, body.scrollTop);
	
	return { left: left, top: top };
});

events.each(function (name) {
	$.ems[name] = function (elem, handler) {
		$.bind(elem, name, handler);	
		return this;
	};
	$.ems['$' + name] = function (elem, handler) {
		$.unbind(elem, name, handler);	
		return this;
	};
});

var bind_ems_to_dollar = function (method, name) {
	if (name.indexOf('_') == 0)
		return;
	Dollar.prototype[name] = function () {
            //if (!this.length) return this;
    		if ( ('_' + name) in $.ems ) {
    			var ret = $.ems['_' + name].apply(this, $.A(arguments));
    			if (ret !== false)
    				return ret;	
    		}
		return method.apply(this, [this[0]].concat($.A(arguments)));	
	};
};

//create element method for Dollar
$.each($.ems,  bind_ems_to_dollar);

$.ems.extend = function (methods) {
	$.extend($.ems, methods);
	$.each(methods, bind_ems_to_dollar);
};

//ie use styleFloat instead cssFloat
var styleFloat = $.Browser.IE ? 'styleFloat' : 'cssFloat';

//get or set element style
//$.css(elem, name, value)
$.css = function ($) {
var defaultView = document.defaultView || {}, _opacity = $.Browser.IE ?
	function (style, value) {
		if (value !== undefined) {
			style.zoom = 1;
			style.filter = (style.filter || "").replace( /alpha\([^)]*\)/, "" ) +
					(parseInt( value ) + '' == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");	
		}		
		return (
				style.filter && style.filter.indexOf("opacity=") >= 0 
				?
				(parseFloat( style.filter.match(/opacity=([^)]*)/)[1]  ) / 100) + '' :
				''
			) || 1.0;		
	}  :
	function (style, value) {
		if (value !== undefined) {
			style.opacity = value;	
		}
		return style.opacity + '' || 1.0;
	};
	
var dimentionStyles = /(width|height|top|left|right|bottom|margin|padding)$/i;
//$.css
return function (elem, name, value) {
	var set = 'string number'.indexOf(typeof value) >= 0, 
		style = elem.style, ret,
		wantPixel = typeof value == 'boolean' ? value : false,
		name = name.replace(/-([a-z])/g, function (all, letter) {
			return letter.toUpperCase();	
		});
	if (/float/.test(name))
		name = styleFloat;
	if (set) {
		if (name == 'opacity')
			_opacity(style, value);
		else {
			//add px to the number value
			if (dimentionStyles.test(name) && /^-?[\d.]+$/.test(value))
				value = value + 'px';
			style[name] = value;
		}
		return $;
	}
	if (name == 'opacity') {
		ret = _opacity(style);
		if ($.isIE)
			return ret;
	} else
		ret = style[name];
	if (!ret || wantPixel) {
		if (defaultView.getComputedStyle) {
			name = name.replace(/([A-Z])/g, "-$1").toLowerCase();
			if (/float/.test(name))
				name = 'float';
			var computedStyle = defaultView.getComputedStyle(elem, null);
			if ( computedStyle )
				ret = computedStyle.getPropertyValue( name );	
		} else if (elem.currentStyle) {
			ret = elem.currentStyle[name];
			// From the awesome hack by Dean Edwards
			// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291

			// If we're not dealing with a regular pixel number
			// but a number that has a weird ending, we need to convert it to pixels
			if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
				// Remember the original values
				var left = style.left, rsLeft = elem.runtimeStyle.left;

				// Put in the new values to get a computed value out
				elem.runtimeStyle.left = elem.currentStyle.left;
				style.left = ret || 0;
				ret = style.pixelLeft + "px";

				// Revert the changed values
				style.left = left;
				elem.runtimeStyle.left = rsLeft;
			}
		}	
	}
	if ('width height'.indexOf(name) >= 0 && !/^\d+px/.test(ret)) {
		var val = $.ems.dimentions(elem)[name];
		(name == 'width' ? ['Left', 'Right'] : ['Top', 'Bottom']).each(function (prop) {
			val -= parseInt($.css(elem, 'padding' + prop, true)) || 0;
			val -= parseInt($.css(elem, 'border' + prop + 'Width', true)) || 0;
		});
		ret = Math.max(0, val);
	}
	if (name == 'opacity' && ret == '') 
		ret = 1.0;
	return ret;
};
}($);

//get or set element attributes
//$.Attr(elem, name, value)
$.attr = function ($) {
	//special attribute defines
	//when not function value, just replace the name
	var special = {
		"for": "htmlFor",
		"class": "className",
		"float": styleFloat,
		cssFloat: styleFloat,
		styleFloat: styleFloat,
		__get1: function (elem, name) { return elem.getAttribute(name, 2) },
		__get2: function (elem, name) { 
			var node = elem.getAttributeNode(name);
          		return node == null ? undefined: node.value;	
		}
	};
	$.extend(special, {
		href: special.__get1,
		src: special.__get1,
		type: special.__get1,
		action: special.__get2	
	});
	'colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
      	'encType maxLength readOnly longDesc cellSpacing cellPadding'.split(/\s/).each(function(pro){
      		special[pro.toLowerCase()] = pro;	
      	});
      	//set attribute in common way
      	var commonSet = function (elem, name, value) {
      		if (name == 'style')
      			$.css(elem, name, value);
      		else if (name in elem)
      			elem[name] = value;
      		else 
      			elem.setAttribute(name, "" + value);
      	};
      	//get attribute in common way
      	var commonGet = function (elem, name) {
      		if (name == 'style')
      			return $.css(elem, name);
      		else if (name in elem)
      			return elem[name];
      		else
      			return elem.getAttribute(name);	
      	};
      	//the $.Attr function
      	return function (elem, name, value) {
            if (!elem) return undefined;
      		var set = value !== undefined, ret;
      		if (typeof special[name] == 'string') {
      			name = special[name];
      		}
      		if (set)
      			commonSet(elem, name, value);
      		if (typeof special[name] == 'function')
      			ret = special[name](elem, name);
      		else
      			ret = commonGet(elem, name);
      		return ret == null ? undefined : ret;
      	}
}($);

//Event fix
//Reference to Prototype-1.6.2.js(www.prototype.org) and jquery-1.3.js(www.jquery.com)
$.Event = {
	BACKSPACE: 8,
	TAB:       9,
	RETURN:   13,
	ESC:      27,
	LEFT:     37,
	UP:       38,
	RIGHT:    39,
	DOWN:     40,
	special: {
		mouseenter: {
			bind: function (elem) {
				if ($.isIE) return false;
				$.bind(elem, 'mouseover', $.Event.special.mouseenter.handler);
				return true;	
			},
			unbind: function (elem) {
				if ($.isIE) return false;
				$.unbind(elem, 'mouseover', $.Event.special.mouseenter.handler);
				return true;
			},
			handler: function (event) {
				var elem = this;
				if (withinElement(event, elem)) return true;
				$.values(($.data(elem, 'events') || {})['mouseenter'] || {}).each(function (handler) {
					handler.call(elem, event);
				});
			}	
		},
		mouseleave: {
			bind: function (elem) {
				if ($.isIE) return false;
				$.bind(elem, 'mouseout', $.Event.special.mouseleave.handler);
				return true;	
			},
			unbind: function (elem) {
				if ($.isIE) return false;
				$.unbind(elem, 'mouseout', $.Event.special.mouseleave.handler);	
				return true;
			},
			handler: function (event) {
				var elem = this;
				if (withinElement(event, elem)) return true;
				$.values(($.data(elem, 'events') || {})['mouseleave'] || {}).each(function (handler) {
					handler.call(elem, event);
				});	
			}
		}
	}
};

//from jquery
var withinElement = function(event, elem) {
	// Check if mouse(over|out) are still within the same parent element
	var parent = event.relatedTarget;
	// Traverse up the tree
	while ( parent && parent != elem ) try { parent = parent.parentNode; } catch(error) { parent = elem; }
	// Return true if we actually just moused on to a sub-element
	return parent == elem;
};

$.Event.fix = function () {
	if ($.Browser.IE) {
		return function (event) {
			if (!event.target)
				event.target = event.srcElement || document;			
			//used only mouseout,mouseover 
			if ( !event.relatedTarget && event.fromElement )
				event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;
			
			// Add which for key events
			if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) )
				event.which = event.charCode || event.keyCode;
			
			// Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
			if ( !event.metaKey && event.ctrlKey )
				event.metaKey = event.ctrlKey;
			
			//add pageX/pageY
			if ( event.pageX == null ) {
				var doc = document.documentElement, body = document.body;
				event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0);
				event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0);
			}
			
			// Add which for click: 1 == left; 2 == middle; 3 == right
			if ( !event.which && event.button )
				event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
			
			event.stopPropagation = function() { this.cancelBubble = true };
      			event.preventDefault = function() { this.returnValue = false };
		};		
	} else {
		Event.prototype = $.Event.prototype || document.createEvent("HTMLEvents").__proto__;
    		return $.nothing;
	}
}();

$.Event.extend = function (event) {
	$.extend(event, $.Event.Methods);	
};

$.Event.Methods = {
	pointer: function() {
		return { x : this.pageX, y : this.pageY };
	},
	stop: function () {
		 this.preventDefault();
      		this.stopPropagation();
	}
};

$.extend($.Event, (function(){
	//default event handler
	function dhandler (event) {
		var events = $.data(this, 'events'), elem = this;		
		if (events && events[event.type]) {
			$.Event.fix(event);
			$.Event.extend(event);
			// check if target is a textnode (safari)
			if (event.target.nodeType == 3)
				event.target = event.target.parentNode;
			$.values(events[event.type]).each(function (handler) { handler.call(elem, event) });
		}
	}
	//get binded event handler
	function getHandler (elem) {
		return function (event) {
			return dhandler.call(elem, event || window.event);	
		}	
	}
	return {
		bind: function (elem, types, handler) {
			if (!elem)
				throw arguments.caller + "bind event on non-element";
			var ghandler = $.data(elem, 'handler') || $.data(elem, 'handler', getHandler(elem)),
				events = $.data(elem, 'events') || $.data(elem, 'events', {});
			if (!handler.guid) handler.guid = $.guid();
			
			//may set multiple types
			types.split(/\s+/).each (function (type) {
				var handlers = events[type];
				if (!handlers)
					handlers = events[type] = {};
				handlers[handler.guid] = handler;
				
				//check if the observer interest on that
				if (!$.Event.special[type] || $.Event.special[type].bind(elem) === false) {
					if (elem.addEventListener)
						elem.addEventListener(type, ghandler, false);
					else if (elem.attachEvent)
						elem.attachEvent("on" + type, ghandler);
				}
			});
			
			elem = null;
			
			return this;
		},
		
		unbind: function (elem, types, handler) {
			var events = $.data(elem, 'events');
			
			types.split(/\s+/).each(function (type) {
				if (events[type]) {
					if (handler)
						delete events[type][handler.guid];
					else
						for (var guids = $.keys(events[type]), length = guids.length; length;)
							delete events[type][guids[--length]];
				}
				if (!$.Event.special[type] || $.Event.special[type].unbind(elem) === false) {
					if (elem.removeEventListener)
						elem.removeEventListener(type, $.data(elem, "handler"), false);
					else if (elem.detachEvent)
						elem.detachEvent("on" + type, $.data(elem, "handler"));	
				}
				if (!$.keys(events[type]).length) {
					delete events[type];		
				}	
			});
			return this;
		}
	};
})());

//override the Function.prototype.bind
$.extend($, {
	bind: $.Event.bind,
	unbind: $.Event.unbind	
});

$.extend($.prototype, {
	bind: function (events, handler) {
		if (this.length)
			$.bind(this[0], events, handler);	
	},
	unbind: function (events, handler) {
		if (this.length)
			$.unbind(this[0], events, handler);	
	}	
});
//Data Interface
function getHttp () {
	var trys = [
		function() {return new XMLHttpRequest()},
      		function() {return new ActiveXObject('Msxml2.XMLHTTP')},
      		function() {return new ActiveXObject('Microsoft.XMLHTTP')}
	];
	for (var i = 0; i < trys.length; i ++) {
		try {
			return trys[i]();
		} catch(e) {}	
	}
	return false;
}

$.Ajax = $({
	__init: function (url, sets) {
		this.sets = $.extend({
			method: 'post',
	  		contentType: 'application/x-www-form-urlencoded',
	  		accept: '*/*',
	  		async: true	
		}, sets);
		this.sets.method = this.sets.method.toLowerCase();
		if (!['get', 'post'].include(this.sets.method))
			this.sets.method = 'post';
		this.http = getHttp();
		if (!this.http)
			return this.dispatch ('UnSupport');
		this.sets.url = url;
		this.send(url);
	},
	send: function (url) {
		var sets = this.sets, http = this.http;
		this.dispatch('Start');
        if (sets.data) {
            var query_params = [];
            $.each(sets.data, function(val,key){
                val = val + '', key = key + '';
                query_params.push(
                '{0}={1}'.format(key.encode(), val.encode())
                );
            });
            query_params = query_params.join('&');
            if (sets.method == 'post') {
                sets.postBody = query_params;
            }
            else {
                url = url 
                    + (url.indexOf('?') >= 0 ? '&' : '?') 
                    + query_params;
            }
        }
		http.open(sets.method, url, sets.async);
		http.onreadystatechange = this.onStateChange.bind(this);
		http.setRequestHeader("Accept", sets.accept);

        // Set header so the called script knows that it's an XMLHttpRequest
        http.setRequestHeader("X-Requested-With", "XMLHttpRequest");

		if (sets.postBody) {
			http.setRequestHeader("Content-type", sets.contentType);
		}
		if (sets.headers) {
			for (var i = 0; i < sets.headers.length; i++) {
				var header = sets.headers[i];
				http.setRequestHeader(header[0], header[1]);	
			}	
		}
		http.send(sets.method == 'post' ? (sets.postBody || "_") : null);
		if (sets.async && sets.timeout)
			this.timer = this.dispatch.bind(this).delay(sets.timeout, 'Timeout');
	},
	
	onStateChange: function () {
		if (this.timeout) return;
		var http = this.http;
    		if (http.readyState == 4) {
    			if (http.status == 200) {
    				this.dispatch('Ok');
    			} else {
    				this.dispatch('Fail');
    			}
    			this.dispatch('End');	
    		}
  	},
  	
  	onTimeout: function () {
  		this.timeout = true;
  		this.timer = null;
  		this.dispatch('End');
  	},
  	
  	onStart: function () { 
  		$.Ajax.Status.activeNum ++;
  		if (!this.sets.guid)
  			this.sets.guid = $.guid();
  		$.Ajax.Status.requests[this.sets.guid] = this.sets;
  	},
  	
  	onEnd: function () {
  		$.Ajax.Status.activeNum --;
  		delete $.Ajax.Status.requests[this.sets.guid];
  		this.http = null;	
  		this.sets = null;
  		if (this.timer) {
  			window.clearTimeout(this.timer);
  			this.timer = null;
  		}
  	},
  	
  	onOk: function () {
  		var data = this.http.responseText;
  		if (this.sets.evalJson)
  			data =  window["eval"]('(' + data + ')');
  		(this.sets['onOk'] || $.nothing)(data, this.http);
  	},
  	
	dispatch: function (event) {
		var method = 'on' + event;
		if (event == 'Ok')
			return this.onOk();
		else
			this.sets[method] && this.sets[method] (this, this.http);
		this[method] && this[method]();
	}	
});
$.Ajax.Status = {
	activeNum: 0,
	requests: {}	
};

$.Json = $({
	__init: function (url, sets) {
		this.sets = $.extend({
		}, sets || {});
		var callbackName = false, t = this;
		//wrap callback function
		if ($.isFunction(this.sets.onOk)) {
			callbackName = $.guid();
			var callback = this.sets.onOk;
			window[callbackName] = function (json) {
				if (t.timeout) return;
				callback(json); 
				window[callbackName] = undefined;
				try { delete window[callbackName] } catch (e) {} 
				if (head) setTimeout(function(){head.removeChild(script)}, 10);
				t.dispatch('End');
			};		
		} else if ($.isString(this.sets.onOk)) 
			//use custom callback name
			callbackName = this.sets.onOk;
			
		if (callbackName) 
			url = url + (url.indexOf('?') == -1 ? '?' : '&') + 'callback=' + callbackName;
			
		var head = document.getElementsByTagName('head') [0];
		var script = document.createElement('script');
		script.src = url;
		
		this.sets.url = url;
		//if callback not wrapped
		if (!callbackName || !$.isFunction(this.sets.onOk)) {
			var done = false;
			script.onload = script.onreadystatechange = function(){
				if (t.timeout) return;
				if ( !done && (!this.readyState || 
						this.readyState == "loaded" || this.readyState == "complete") ) {
					done = true;
					t.dispatch('End');
					head.removeChild( script );
				}
			};	
		}
		this.dispatch('Start');
		head.appendChild(script);
		if (this.sets.timeout)
			this.timer = setTimeout(function () {
				t.timer = null;
				t.timeout = true;
				head.removeChild(script);
				t.dispatch('End');
			}, this.sets.timeout * 1000);	
	},
	
	onStart: function () {
		$.Json.Status.activeNum ++;
		if (!this.sets.guid)
			this.sets.guid = $.guid();
		$.Json.Status.requests[this.sets.guid] = this.sets;	
	},
	
	onEnd: function () {
		if (this.timer) {
			window.clearTimeout(this.timer);	
			this.timer = null;
		}
		$.Json.Status.activeNum --;
		delete $.Json.Status.requests[this.sets.guid];
		this.sets = null;
	},
	
	dispatch: function (event) {
		var method = 'on' + event;
		this.sets[method] && this[method]();
		this[method] && this[method]();
	}
});
$.Json.Status = {
	activeNum: 0,
	requests: {}	
};

//extend to $
['get', 'post', 'json'].each(function(type){
	var DataClass = $.Ajax;
	if (type == 'json')
		DataClass = $.Json;
	$[type] = function (url, sets) {
		if ($.isFunction (sets) ) {
			sets = {
				onOk: sets	
			};	
		}
		sets.method = type;
		return new DataClass(url, sets);
	}	
});

//Html node builder
$.Builder = function () {
var simpleTagMatch = /^<?(\w+)(\/>)?$/;
var tagMatch = /^<(\w+)[^>]*?\/?>((.|\s)*<\/[^>]+>)?$/;
return {
	//base jquery funciton "clean"
	node: function (html) {
		if (simpleTagMatch.test(html))
			return document.createElement(RegExp.$1);
		if (!tagMatch.test(html))
			return null;
		var tags = RegExp.$1.toLowerCase(), div = document.createElement("div");
		
		var wrap =
			// option or optgroup
			!tags.indexOf("opt") &&
			[ 1, "<select multiple='multiple'>", "</select>" ] ||
			
			!tags.indexOf("leg") &&
			[ 1, "<fieldset>", "</fieldset>" ] ||
			
			tags.match(/^(thead|tbody|tfoot|colg|cap)/) &&
			[ 1, "<table>", "</table>" ] ||
			
			!tags.indexOf("tr") &&
			[ 2, "<table><tbody>", "</tbody></table>" ] ||
			
		 	// <thead> matched above
			(!tags.indexOf("td") || !tags.indexOf("th")) &&
			[ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] ||
			
			!tags.indexOf("col") &&
			[ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] ||

			// IE can't serialize <link> and <script> tags normally
			$.Browser.IE &&
			[ 1, "div<div>", "</div>" ] ||
			
			[ 0, "", "" ];

		// Go to html and back, then peel off extra wrappers
		div.innerHTML = wrap[1] + html + wrap[2];
		
		// Move to the right depth
		while ( wrap[0]-- )
			div = div.lastChild;
		
		// Remove IE's autoinserted <tbody> from table fragments
		if ( $.Browser.IE ) {
			
			// String was a <table>, *may* have spurious <tbody>
			var tbody = !tags.indexOf("table") && tags.indexOf("tbody") < 0 ?
				div.firstChild && div.firstChild.childNodes :
				
				// String was a bare <thead> or <tfoot>
				wrap[1] == "<table>" && tags.indexOf("tbody") < 0 ?
					div.childNodes :
					[];
		
			for ( var j = tbody.length - 1; j >= 0 ; --j )
				if ( tbody[ j ].tagName == "TBODY" && !tbody[ j ].childNodes.length )
					tbody[ j ].parentNode.removeChild( tbody[ j ] );
		}
		
		var elem = div.firstChild || null;
		if (elem && elem.tagName != tags.toUpperCase()) {
			elem = div.getElementsByTagName(tags)[0];	
		}
		return elem || null;
	},
	
	text: function (str) {
		return document.createTextNode(str); 
	}	
};
}();

//extend to $
$.extend($, {
	node: $.Builder.node,
	text: $.Builder.text	
});

//code based on scriptaculous(http://script.aculo.us/)
$.Effect = function () {
	var effects = [], queues = {};
	var interval = null;
	//interval handler
	function loopHandler () {
		var timePos = +new Date;
    		for (var i = 0; i < effects.length; i ++) {
    			var ef = effects[i];
    			if(timePos >= ef.finishOn) {
					ef.status = 'finish';
        			remove(ef, true);
        			return;  
      			}
      			if (ef.status == 'idle') {
      				ef.onStart && ef.onStart();
      				ef.status = 'running';	
      			}
    			var pos = (timePos - ef.startOn) / (ef.finishOn - ef.startOn);
    			var frame = Math.round(pos * ef.totalFrames);
      			if(frame > ef.currentFrame) {
      				pos = ef.transition(pos);
      				pos = ef.from + ((ef.to - ef.from) * pos);
        			ef.render(pos * ef.factor);
        			ef.currentFrame = frame;
      			}
    		}
	}
	
	//remove a effect
	function remove (effect, toEnd) {
		var ret = 0, length = effects.length, item;
		while (length--) {
			item = effects[length];
			if (item == effect || item.guid == effect) {
				if (toEnd) {
					item.render(
						(item.from + ((item.to - item.from) * item.transition(1))) * item.factor
					);
					item.onFinish && item.onFinish();
					item.status = 'finish';
				}
				item.onRemove && item.onRemove();
				ret ++;
				effects.splice(length, 1);
			}
		}
    		if (effects.length == 0) {
      			clearInterval(interval);
      			interval = false;
    		}
    		return ret;
	}
	
	//add a effect
	function add (effect) {
		if (effect.onAdd) {
			effect.onAdd();
			delete effect.onAdd;
		}
		var timestamp = +new Date;
		effect = $.extend({
			transition:	$.Effect.Transitions.sinoidal,
			duration:	1.0,   // seconds
			fps:      	25.0,  // max. 25fps due to Effect.Queue implementation
		  	from:       	0.0,
		  	to:         	1.0,
		  	delay:      	0.0,
		  	factor: 1
		}, effect);
    		effect.startOn  = effect.delay * 1000 + timestamp;
    		effect.finishOn = effect.startOn + effect.duration * 1000;
    		effect.currentFrame = 0;
    		effect.totalFrames = effect.fps * effect.duration;
    		if ($.isString(effect.transition))
    			effect.transition = $.Effect.Transitions[effect.transition];
    		effect.status = 'idle';
    		effects.push(effect);
    		if(!interval)
    			interval = window.setInterval(loopHandler, 40);
    		return effect;
	}
	
	function addToQueue (effect) {
		var queueId = effect.queue, originOnRemove = effect.onRemove, queue = queues[queueId];
		effect.onRemove = function () {
			if (queues[queueId] && queues[queueId].data.length) {
				var effect = queues[queueId].data.shift();
				queues[queueId].current = effect;
				add (effect);
			} else {
				delete queues[queueId];
			}
			originOnRemove && originOnRemove();
		};
		if (queue) {
			queue.data.push(effect);	
		} else {
			queue = queues[queueId] = {};
			queue.data = [];
			queue.current = effect;
			add(effect);
		}
	}
	function removeQueue (queueId) {
		var queue = queues[queueId];
		if (queue) {
			var current = queue.current;
			delete queues[queueId];
			//remove(current, toEnd);
		}
	}
	function status () {
		console.log('effects:' + effects.length);
		console.log('queues:' + $.keys(queues).length);	
	}
	return {
		add: add,
		addToQueue: addToQueue,
		removeQueue: removeQueue,
		remove: remove,
		status: status
	}	
}();

$.Effect.extend = function (effects) {
	var toExtend = {};
	$.each(effects, function (_sets, name) {
		if (!_sets.style)
			_sets = { style : _sets };
		toExtend[name] = function (elem) {
			var sets = $.extend({}, _sets), args = $.A(arguments);
			args.shift(); //elem
			if (args[0] && typeof args[0] == 'number') {
				sets.duration = args.shift();
			}
			var onFinish = args.shift(), customSets = args.shift();
			if (onFinish) {
				var originOnFinish = sets.onFinish;
				sets.onFinish = function () {
					onFinish.call(elem);
					originOnFinish && originOnFinish();	
				};
			}
			if (customSets) $.extend(sets, customSets);
			return $.ems.effect.call(this, elem, sets);
		};	
	});
	$.ems.extend(toExtend);
};

$.Effect.Transitions = {
	linear: function (pos) { return pos; },	
	sinoidal: function (pos) { return (-Math.cos(pos * Math.PI) / 2) + 0.5; },
	reverse: function (pos) { return 1 - pos; },
	flicker: function (pos) { return ((-Math.cos(pos * Math.PI) / 4) + 0.75) + Math.random() / 4; },
	wobble: function (pos) { return (-Math.cos(pos * Math.PI * (9 * pos)) / 2) + 0.5; },
	pulse: function (pos) { return (Math.floor(pos * 10) % 2 == 0 ?  (pos * 10 - Math.floor(pos * 10)) : 1 - (pos * 10 - Math.floor(pos * 10))); }
};

function extendElementEffect(sets) {
	var elem = this, $elem = $(elem);
	if (!sets.style) 	return;
	var styles = sets.style, names = [];
	if (styles.START) {
		var originOnStart = sets.onStart;
		sets.onStart = function () {
			typeof styles.START == 'function' ?
				styles.START.call(elem) :
				$elem.css(styles.START);
			originOnStart && originOnStart.call(elem);
		};
	} else
		sets.onStart && (sets.onStart = sets.onStart.bind(elem));	
	if (styles.END) {
		var originOnFinish = sets.onFinish;
		sets.onFinish = function () {
			typeof styles.END == 'function' ?
				styles.END.call(elem) :
				$elem.css(styles.END);
			originOnFinish && originOnFinish.call(elem);
		};	
	} else
		sets.onFinish && (sets.onFinish = sets.onFinish.bind(elem));
	styles.INIT && (typeof styles.INIT == 'function' ? styles.INIT.call(elem, styles) : $elem.css(styles.INIT));
	$.each(styles, function (val, name) {
		if (' INIT START END '.indexOf(' ' + name + ' ') >= 0) return;
        if (typeof val == 'function') {
            val = val.call(elem);
        }
		var from = parseFloat($.css(elem, name, true)) || 0, to,
		takens = val.toString().match(/^([-+]=)?([\d+-.]+)(.*)$/);
		if (takens) {
			to = parseFloat(takens[2]), unit = takens[3] || 'px';	
			if (unit != 'px') {
				$.css(elem, name, to + unit);
				from = (to / parseFloat($.css(elem, name, true))) * from;
				$.css(elem, name, from + unit);
			}
			if (takens[1])
				to = (takens[1] == '-=' ? -1 : 1) * to + from;
			names.push({
				name : name,
				from : from,
				to : to,
				unit : takens[3] || ''	
			});
		}
	});
	var handle = sets.handle;
	sets.render = function (pos) {
		for (var i = 0, n = names.length; i < n; i ++) {
			var style = names[i],
			value = style.from + (style.to - style.from) * pos;
			//console.log('set {0} : {1}; from : {2}, to : {3}'.format(names[i], value, style.from, style.to));
			if (style.unit)
				value += style.unit;
			$.css(elem, style.name, value);
		}
		handle && handle.call(elem, pos);
	};
}

var _display_styles = 
['paddingLeft', 'paddingTop', 'paddingBottom', 'paddingRight',
'marginLeft', 'marginTop', 'marginBottom', 'marginRight',
'width', 'height'];

$.Effect.extend({
    'eshow': {
        INIT: function(styles){
            var origin = {}, elem = this;
            _display_styles.each(function(name) {
                origin[name] = elem.style[name];
                styles[name] = $.css(elem, name, true);
                $.css(elem, name, '0px');
            });
            origin['overflow'] = elem.style.overflow;
            $.data(elem, 'eshow', origin);

            $.css(elem, 'opacity', '0').
            css(elem, 'display', 'block').
            css(elem, 'overflow', 'hidden');
        },
        opacity: 1,
        END: function () {
            var elem = this, origin = $.data(elem, 'eshow');

            $.each(origin, function(val, name) {
                elem.style[name] = val;
            });
        }
    },

    'ehide': {
        INIT: function (styles) {
            var origin = {}, elem = this;
            _display_styles.each(function(name) {
                origin[name] = elem.style[name];
                styles[name] = '0px';
            });
            origin['overflow'] = elem.style.overflow;
            $.data(elem, 'ehide', origin);
        },
        opacity: 0,
        END: function () {
            var elem = this, origin = $.data(elem, 'ehide');
            $.css(elem, 'display', 'none').css(elem, 'opacity', '1');

            $.each(origin, function(val, name) {
                elem.style[name] = val;
            });
        }
    }
});

$.ready = function (func) {
	if ($.isReady)
		func();
	if (!$.ReadyList)
		$.ReadyList = [];
	$.ReadyList.push(func);
	bindReady();
	return $;
};

//bind dom ready
//code from : http://webreflection.blogspot.com/search?q=onContent
function bindReady () {
	if (bindReady.isBound)
		return;
	bindReady.isBound = true;
	var callback = function () {
		if ($.isReady) 
			return; 
		if ($.isIE) {
			try {
				document.documentElement.doScroll("left");
			} catch( error ) {
				setTimeout( arguments.callee, 0 );
				return;
			}
		}
		$.isReady  = true;
		$.ReadyList.each(function (f) { f() });
		delete $.ReadyList ;
	};
	__onload__={
        		E:function(){window.onload=null;callback()},
        		IES:function(e){if(e.readyState==="complete")this.E()}
    	}; 
    	onload=callback; 
    	if ($.isWebKit || ($.isOpera && parseInt($.browserVersion) < 9))  
    		(function(){/loaded|complete/.test(document.readyState)?__onload__.E():setTimeout(arguments.callee,1)})(); 
    	else if (document.addEventListener) 
    		document .addEventListener ('DOMContentLoaded', __onload__ .E, false);
    	else if (!$.isOpera || parseInt($.browserVersion) >= 9)
    		document.write('<SCR'+'IPT onreadystatechange="__onload__.IES(this)" src="javascript:void(0)" defer="defer"></SCR'+'IPT>');	
}
})(window);
